package com.tian.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.tian.common.CommonResult;
import com.tian.dto.ChargeStationCountRespDto;
import com.tian.dto.StationInfoIndexRespDto;
import com.tian.entity.ChargeStationGun;
import com.tian.entity.ChargeStationInfo;
import com.tian.enums.ChargerStatusEnum;
import com.tian.es.entity.ChargeStationLocation;
import com.tian.es.entity.GeoLocationPoiVo;
import com.tian.es.entity.StationNearByPoiParam;
import com.tian.es.service.ChargeStationEsService;
import com.tian.util.StationRedisPreKey;
import com.tian.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.FuzzyQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.redisson.api.RBucket;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * {@code @description:} 站点数据存放到ES中
 *
 * @author tianwc 公众号：Java后端技术全栈
 * 在线刷题 1200+java面试题和1000+篇技术文章：<a href="https://woaijava.cc/">博客地址</a>
 * {@code @date:} 2024-07-23 18:46
 * {@code @version:} 1.0
 */
@Slf4j
@Service
public class ChargeStationEsServiceImpl implements ChargeStationEsService {

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Resource
    private RedissonClient redissonClient;

    /**
     * 新增充电站信息
     */
    @Override
    public void add(ChargeStationLocation chargeStationLocation) {
        //判断是否存在次索引，不存在则创建
        IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(ChargeStationLocation.class);
        if (!indexOperations.exists()) {
            indexOperations.create();
            Document mapping = indexOperations.createMapping();
            indexOperations.putMapping(mapping);
        }
        elasticsearchRestTemplate.save(chargeStationLocation);
    }

    @Override
    public void delete(Integer id) {
        elasticsearchRestTemplate.delete(id.toString(), ChargeStationLocation.class);
    }

    @Override
    public CommonResult<Boolean> deleteIndex() {
        IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(ChargeStationLocation.class);
        indexOperations.delete();
        return CommonResult.success(Boolean.TRUE);
    }

    @Override
    public CommonResult<List<StationInfoIndexRespDto>> getNearByLocation(StationNearByPoiParam stationNearByPoiParam) {

        List<StationInfoIndexRespDto> stationInfoIndexRespDtoList = new ArrayList<>();

        List<GeoLocationPoiVo> geoLocationPoiVoList = new ArrayList<>();
        Pageable pageable = PageRequest.of(0, stationNearByPoiParam.getSize());

        // 搜索字段为 location
        GeoDistanceQueryBuilder geoBuilder = new GeoDistanceQueryBuilder("geoPoint");
        //指定从哪个位置搜索
        geoBuilder.point(stationNearByPoiParam.getLat(), stationNearByPoiParam.getLon());
        //指定搜索多少km
        geoBuilder.distance(stationNearByPoiParam.getDistance(), DistanceUnit.KILOMETERS);

        BoolQueryBuilder boolQueryBuilder = null;
        if (!StringUtil.isBlank(stationNearByPoiParam.getStationName())) {
            FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("name", stationNearByPoiParam.getStationName());
            boolQueryBuilder = QueryBuilders.boolQuery()
                    .must(geoBuilder)
                    .must(fuzzyQueryBuilder);
        } else {
            boolQueryBuilder = QueryBuilders.boolQuery()
                    .must(geoBuilder);
        }
        // 距离排序
        GeoDistanceSortBuilder sortBuilder = new GeoDistanceSortBuilder("geoPoint", stationNearByPoiParam.getLat(), stationNearByPoiParam.getLon());
        //升序
        sortBuilder.order(SortOrder.ASC);
        //搜索
        SearchHits<ChargeStationLocation> searchHits = elasticsearchRestTemplate
                .search(new NativeSearchQueryBuilder()
                        .withPageable(pageable)
                        .withFilter(boolQueryBuilder)
                        .withSort(sortBuilder)
                        .build(), ChargeStationLocation.class);
        //获取充电站信息数据
        searchHits.forEach(searchHit -> {
            double calculate = GeoDistance.PLANE.calculate(searchHit.getContent().getGeoPoint().getLat(), searchHit.getContent().getGeoPoint().getLon(), stationNearByPoiParam.getLat(), stationNearByPoiParam.getLon(), DistanceUnit.METERS);
            GeoLocationPoiVo geoLocationPoiVo = new GeoLocationPoiVo();
            BeanUtils.copyProperties(searchHit.getContent(), geoLocationPoiVo);
            geoLocationPoiVo.setSpecificDistance(calculate + "米");
            geoLocationPoiVoList.add(geoLocationPoiVo);
        });
        geoLocationPoiVoList.forEach(stationInfoGeo -> {
            int stationId = Integer.parseInt(stationInfoGeo.getStationId());
            RBucket<String> stationBucket = redissonClient.getBucket(StationRedisPreKey.STATION_PRE_KEY + stationId);
            ChargeStationInfo chargeStationInfo = JSON.parseObject(stationBucket.get(), ChargeStationInfo.class);

            StationInfoIndexRespDto stationInfoIndexRespDto = new StationInfoIndexRespDto();

            RList<String> list = redissonClient.getList(StationRedisPreKey.STATION_GUN_LIST_KEY + stationId);

            AtomicInteger freeTotal = new AtomicInteger(0);
            AtomicInteger chargingTotal = new AtomicInteger(0);

            list.forEach(gunNo -> {
                RBucket<String> bucket = redissonClient.getBucket(StationRedisPreKey.STATION_GUN_NO_PRE_KEY + gunNo);
                ChargeStationGun chargeStationGun = JSON.parseObject(bucket.get(), ChargeStationGun.class);
                if (chargeStationGun.getStatus() == ChargerStatusEnum.IDLE.getChargerStatus()) {
                    freeTotal.getAndIncrement();
                } else {
                    chargingTotal.getAndIncrement();
                }
            });

            BeanUtils.copyProperties(chargeStationInfo, stationInfoIndexRespDto);
            stationInfoIndexRespDto.setStationFreeTotal(freeTotal.get());
            stationInfoIndexRespDto.setStationUsingTotal(chargingTotal.get());
            stationInfoIndexRespDtoList.add(stationInfoIndexRespDto);
        });
        //站点信息中使用中的充电枪个数需要统计--设备状态
        return CommonResult.success(stationInfoIndexRespDtoList);
    }

    /**
     * 获取充电站总数
     */
    @Override
    public CommonResult<ChargeStationCountRespDto> count() {
        ChargeStationCountRespDto chargeStationCountRespDto = new ChargeStationCountRespDto();
        chargeStationCountRespDto.setStationCount(100);
        chargeStationCountRespDto.setChargeCount(3000);
        chargeStationCountRespDto.setChargeAvailableCount(2890);
        return CommonResult.success(chargeStationCountRespDto);
    }
}
